#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QRegularExpression>
#include <QDateTime>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QValueAxis>
#include <csignal>  // 添加这个头文件
#include <QNetworkInterface>  // 网络接口相关
#include <QHostAddress>       // IP地址处理
#include <QDebug>

void MainWindow::parseProtocol(const QString &command, QTcpSocket *client)
{
    // 解析指令格式: <ID=xxx><PARAM1=value1><PARAM2=value2>;
    QRegularExpression re("<([^>]+)>");
    QRegularExpressionMatchIterator i = re.globalMatch(command);
    LOG_CAMERA("Received OCS command: " + command);

    QMap<QString, QString> params;
    while (i.hasNext()) {
        QRegularExpressionMatch match = i.next();
        QString segment = match.captured(1);
        int eqPos = segment.indexOf('=');
        if (eqPos != -1) {
            QString key = segment.left(eqPos).trimmed();
            QString value = segment.mid(eqPos+1).trimmed();
            params[key] = value;
        }
    }

    // 根据ID分发处理
    QString cmdId = params.value("ID");
    if (cmdId == "300") {
        handleCommand300(params, client);
    } else if (cmdId == "301") {
        handleCommand301(params, client);
    } else if (cmdId == "302") {
        handleCommand302(params, client);
    } else if (cmdId == "303") {
        handleCommand303(params, client);
    } else if (cmdId == "304") {
        handleCommand304(params, client);
    } else {
        sendResponseThreadSafe(QString("<ID=%1><INFO=Error><MSG=Unsupported_command>").arg(cmdId), client);
    }
}


void MainWindow::handleCommand300(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 格式: <ID=300><CamID=25078><INI=1><COOLING=1><COOL_TEM=-65>
    if (!params.contains("CamID") || !params.contains("COOLING") || !params.contains("COOL_TEM")) {
        sendResponseThreadSafe("<ID=300><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    QString camId = params["CamID"];
    int cooling = params["COOLING"].toInt();
    double targetTemp = params["COOL_TEM"].toDouble();

    // 1. 先返回确认响应
    sendResponseThreadSafe("<ID=300><INFO=OK>", client);

    // 2. 发送温控指令
    QString tempCmd = QString("temp_set:%1").arg(targetTemp);
    sendActionCommandToMCU(tempCmd);

    // 3. 异步等待温度稳定后返回最终响应
    QTimer::singleShot(1000, [this, client, camId]() {
        QString response = QString("<ID=300><CamID=%1><INFO=OK><ACTION=DONE><INI=1><CUR_TEM=%2><SENDTIME=%3>")
        .arg(camId)
            .arg(hwStatus.currentTemp, 0, 'f', 2)  // 使用CCD1-CCD4的平均温度
            .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
        sendResponseThreadSafe(response, client);
    });
}

void MainWindow::handleCommand301(const QMap<QString, QString> &params, QTcpSocket *client)
{
    if (!client) return;
    /*
    协议规范：
    客户端指令格式: <ID=301><CamID=%s>;
    服务端响应格式:
      1. 先返回确认: <ID=301><INFO=OK>
      2. 实际状态返回:
         <ID=301><INFO=%s><ACTION=DONE><STATUS=%d><CUR_TEM=%f><TEM_STATUS=%d><SENDTIME=...>;
      特殊字段:
        STATUS: 相机状态(0-5)
        TEM_STATUS: 制冷稳定状态(0=不稳定,1=稳定)
    */

    //  参数校验
    if (!params.contains("CamID")) {
        sendResponseThreadSafe("<ID=301><INFO=Error><MSG=Missing_CamID>", client);
        client->disconnectFromHost(); // 立即断开连接
        return;
    }
    QPointer<QTcpSocket> safeClient(client);
    QString camId = params["CamID"];
    // 1. 发送温度查询指令
    //sendActionCommandToMCU("temp_read");

    // 2. 立即返回确认响应
    sendResponseThreadSafe("<ID=301><INFO=OK>", client);
    //client->disconnectFromHost(); // 立即断开连接
    int current_status = -1;
    switch(cameraState.status) {
    case 0:  // 空闲
        current_status = 0;
        break;
    case 1:  // 曝光中
        current_status = 2;
        break;
    case 2:  // 读出中
        current_status = 3;
        break;
    case -1:
        current_status = 5;
        break;
    default:
        current_status = 5;
    }

    // 3. 异步等待温度数据更新后返回最终响应
    QTimer::singleShot(500, this, [this, safeClient, camId, current_status]() {
        //QString response = QString("<ID=301><CamID=%1><INFO=OK><ACTION=DONE><STATUS=%2><CUR_TEM=%3><CUR_VACCUM=%4><TEM_STATUS=%5><SENDTIME=%6>")
        if (!safeClient) {
            qDebug() << "Client connection already closed";
            return;
        }
        QString response = QString("<ID=301><CamID=%1><INFO=OK><ACTION=DONE><STATUS=%2><CUR_TEM=%3><CUR_VACCUM=%4><TEM_STATUS=%5><SENDTIME=%6>")
        .arg(camId)
            .arg(static_cast<int>(current_status))
            .arg(hwStatus.currentTemp, 0, 'f', 2)  // 使用CCD1-CCD4的平均温度
            .arg(QString::number(hwStatus.currentVacuum, 'e', 3))
            .arg(hwStatus.tempStabilized ? 1 : 0)
            .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        sendResponseThreadSafe(response, safeClient);
        //safeClient->disconnectFromHost(); // 立即断开连接
    });

    // 4. 状态记录（调试用）
    LOG_CAMERA(QString("Camera status query response [Cam:%1][Status:%2][Temp:%3][Stable:%4]")
                   .arg(camId)
                   .arg(cameraState.status)
                   .arg(hwStatus.currentTemp)
                   .arg(hwStatus.tempStabilized));
}

void MainWindow::handleCommand302(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 1. 参数校验
    if (!params.contains("CamID") || !params.contains("IMAGENAME") ||
        !params.contains("TIME") || !params.contains("EXP_TYPE") ||
        !params.contains("IMG_INFO")) {
        sendResponseThreadSafe("<ID=302><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    // 2. 检查相机状态
    if (cameraState.status != Idle) {
        sendResponseThreadSafe("<ID=302><INFO=Error><MSG=Camera is busy>", client);
        return;
    }

    // 清空已接收的tag
    m_receivedTags.clear();
    m_convertedTags.clear();

    // 3. 解析参数
    QString camId = params["CamID"];
    QString imageName = params["IMAGENAME"];
    double expTime = params["TIME"].toDouble();
    QString expType = params["EXP_TYPE"];
    QString imgInfo = params["IMG_INFO"];
    bool dump = params.value("DUMP", "1") == "1";
    bool pinning = params.value("PINNING", "1") == "1";


    int index = -1;
    if (expType == "sc") {
        index = ui->comBoxframetype->findData("sc");
    } else if (expType == "bs") {
        index = ui->comBoxframetype->findData("bs");
    } else if (expType == "dk") {
        index = ui->comBoxframetype->findData("dk");
    } else if (expType == "ts") {
        index = ui->comBoxframetype->findData("ts");
    } else if (expType == "sf") {
        index = ui->comBoxframetype->findData("sf");
    } else if (expType == "df") {
        index = ui->comBoxframetype->findData("df");
    } else if (expType == "fs") {
        index = ui->comBoxframetype->findData("fs");
    }
    if (index != -1) {
        QMetaObject::invokeMethod(this, [this, index]() {
            ui->comBoxframetype->setCurrentIndex(index);
        }, Qt::QueuedConnection);
    }

    // 4. 立即返回确认响应
    sendResponseThreadSafe("<ID=302><INFO=OK>", client);

    // 5. 记录曝光开始时间（ISO 8601格式）
    QDateTime expStartTime = QDateTime::currentDateTime();
    QString expStartTimeStr = expStartTime.toString("yyyy-MM-dd hh:mm:ss.zzz");

    // 6. 设置曝光参数
    currentBaseFilename = imageName;
    ui->lineEditBaseFilename->setText(currentBaseFilename);
    ui->doubleSpinBoxExposureTime->setValue(expTime);
    currentExposureTime = expTime;
    currentProgressTotalTime = expTime + 0.1; // 曝光时间估算
    progressStartTime = QDateTime::currentMSecsSinceEpoch();

    // 7. 解析并保存IMG_INFO
    QJsonParseError jsonError;
    QJsonDocument doc = QJsonDocument::fromJson(imgInfo.toUtf8(), &jsonError);
    if (!doc.isNull() && doc.isObject()) {
        QJsonObject imgInfoObj = doc.object();
        if (imgInfoObj.contains("IMG_NUM")) {
            // 更新UI中的IMG_NUM显示
            LOG_CAMERA(QString("enter set img_num: [") + imgInfoObj["IMG_NUM"].toString() + "]");
            QString imgNum = imgInfoObj["IMG_NUM"].toString();
            ui->lineEditImgNum->setText(imgNum);
            // LOG_CAMERA(imgNum);
        }
        if (imgInfoObj.contains("OBJ_NAME")) {
            QString objectname = imgInfoObj["OBJ_NAME"].toString();
            ui->lineEditObjName->setText(objectname);
        }
        if (imgInfoObj.contains("FW_NAME")) {
            QString fwname = imgInfoObj["FW_NAME"].toString();
            ui->lineEditFwName->setText(fwname);
        }
        // 同时更新本地计数器，保持同步
        //m_localImageCounter = imgNum.toInt();
        hwStatus.currentImgInfo = imgInfoObj;
    } else {
        LOG_CAMERA(QString("IMG_INFO Parsing failed: %1").arg(jsonError.errorString()));
    }

    int darkMode = (expType == "bs" || expType == "dk") ? 1 : 0;

    // 0814 add
    fitsSavePath = ui->lineEditFitsPath->text();
    QString fitsbasename = imageName;
    if(fitsbasename.endsWith(".fits")) {
        fitsbasename.chop(5);
    }
    currentBaseFilename = fitsbasename;
    ui->lineEditBaseFilename->setText(currentBaseFilename);
    QString fitspathcmd = QString("get_frame_290_subps:$filename=%1$path=%2").arg(fitsbasename).arg(fitsSavePath);
    sendCommandToSlave1(fitspathcmd, statusLeds.slave[0].ip);
    sendCommandToSlave2(fitspathcmd, statusLeds.slave[1].ip);
    sendCommandToSlave3(fitspathcmd, statusLeds.slave[2].ip);
    sendCommandToSlave4(fitspathcmd, statusLeds.slave[3].ip);

    // 8. 发送相机曝光指令（通过UDP）
    QString tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    QString expCmd = QString("get_frame_290:$tag=%1$dark=%2$pinning=%3$dump=%4$exp=%5")
                         .arg(tag)
                         .arg(darkMode)  // 暗场标记
                         .arg(pinning ? 1 : 0)
                         .arg(dump ? 1 : 0)
                         .arg(expTime, 0, 'f', 4);
    sendCameraActionCommand(expCmd);

    m_currentExpInfo.isManual = false;
    m_currentExpInfo.readoutRate = ui->labelCurrentReadoutRate->text();
    m_currentExpInfo.setexptime = expTime;

    // 存储响应信息
    QString tag1 = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    m_pendingResponse = {
        client,
        params["CamID"],
        params["TIME"].toDouble(),
        QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"),
        params["IMAGENAME"]
    };

    // 9. 更新状态
    //hwStatus.cameraState = Exposing;
    // 初始化进度控制
    progressStartTime = QDateTime::currentMSecsSinceEpoch();
    currentExposureTime = expTime;

    // 设置进度条范围
    ui->ExposureprogressBar->setRange(0, 100);
    ui->ExposureprogressBar->setValue(0);

    // 启动定时器
    progressTimer->start(200); // 200ms更新一次

    // 10. 记录日志
    LOG_CAMERA(QString("Start exposure [Cam:%1][Exp:%2s][Type:%3][File:%4]")
                    .arg(camId).arg(expTime).arg(expType).arg(imageName));
}



void MainWindow::handleCommand303(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 格式: <ID=303><CamID=25078>
    if (!params.contains("CamID")) {
        sendResponseThreadSafe("<ID=303><INFO=Error><MSG=Missing_CamID>", client);
        return;
    }

    // 1. 返回确认响应
    sendResponseThreadSafe("<ID=303><INFO=OK>", client);

    // 2. 发送中止曝光指令
    sendCameraActionCommand("abort_290");

    // 3. 更新状态
    //hwStatus.cameraState = MainWindow::Idle;
    progressTimer->stop();
    ui->ExposureprogressBar->setValue(0);

    // 4. 返回完成响应
    QString response = QString("<ID=303><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
                           .arg(params["CamID"])
                           .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
    sendResponseThreadSafe(response, client);
}
/*
void MainWindow::handleCommand304(const QMap<QString, QString> &params, QTcpSocket *client)
{

        // 1. 参数校验
        if (!params.contains("CamID") || !params.contains("FITS_Header") || !params.contains("FITS_Name")) {
            throw std::runtime_error("缺少必要参数");
        }

        QString camId = params["CamID"];
        QString fitsHeader = params["FITS_Header"];
        QString fitsName = params["FITS_Name"];

        // 2. 验证JSON格式
        QJsonParseError jsonError;
        QJsonDocument doc = QJsonDocument::fromJson(fitsHeader.toUtf8(), &jsonError);
        if (doc.isNull()) {
            throw std::runtime_error(QString("无效的JSON格式: %1 (位置:%2)")
                                         .arg(jsonError.errorString())
                                         .arg(jsonError.offset).toStdString());
        }

        // 3. 构建文件路径
        QString fitsFilePath = QDir(fitsSavePath).absoluteFilePath(fitsName);
        if (!fitsFilePath.endsWith(".fits")) {
            fitsFilePath += ".fits";
        }

        // 4. 验证文件存在性
        if (!QFileInfo(fitsFilePath).exists()) {
            throw std::runtime_error(QString("文件不存在: %1").arg(fitsFilePath).toStdString());
        }
        // 5. 解析FITS Header
        QJsonObject headerObj = QJsonDocument::fromJson(fitsHeader.toUtf8()).object();
        if (headerObj.isEmpty()) {
            QString errorResponse = QString("<ID=304><CamID=%1><INFO=Error><MSG=Invalid_FITS_Header><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            sendResponse(errorResponse, client);
            return;
        }
        // 6. 同步更新FITS头信息（在主线程执行）
        bool success = false;
        QString errorMsg;
        success = updateFitsHeader(fitsFilePath, headerObj);
        // 7. 构造响应
        QString response;
        if (success) {
            response = QString("<ID=304><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        } else {
            if (errorMsg.isEmpty()) errorMsg = "头信息更新失败";
            response = QString("<ID=304><CamID=%1><INFO=Error><MSG=%2><SENDTIME=%3>;")
                           .arg(camId)
                           .arg(errorMsg)
                           .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
        }

        // 8. 发送响应
        sendResponse(response, client);

        LOG_GENERAL(QString("FITS头信息更新 [Cam:%1][File:%2][Success:%3]")
                     .arg(camId).arg(QFileInfo(fitsFilePath).fileName()).arg(success));

        // 6. 在后台线程中更新FITS头信息

        //QtConcurrent::run([this, fitsFilePath, headerObj, camId, client]() {
            //bool success = updateFitsHeader(fitsFilePath, headerObj);

            // 7. 构造最终响应
            //QString response;
            //if (success) {
                //response = QString("<ID=304><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
                //.arg(camId)
                //    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            //} else {
              //  response = QString("<ID=304><CamID=%1><INFO=Error><MSG=Header_update_failed><SENDTIME=%2>;")
               // .arg(camId)
                //    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
           // }

            // 注意：需要在主线程发送响应
            //QMetaObject::invokeMethod(this, [this, client, response]() {
               // sendResponse(response, client);
            //});

           // LOG_GENERAL(QString("FITS头信息更新 [Cam:%1][File:%2][Success:%3]")
            //             .arg(camId).arg(QFileInfo(fitsFilePath).fileName()).arg(success));
       // });
}*/
void MainWindow::handleCommand304(const QMap<QString, QString>& params, QTcpSocket* client)
{
    // 1. 参数校验
    if (!params.contains("CamID") || !params.contains("FITS_Header") || !params.contains("FITS_Name")) {
        sendResponseThreadSafe("<ID=304><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    QString camId = params["CamID"];
    QString fitsHeader = params["FITS_Header"];
    QString fitsName = params["FITS_Name"];

    // 2. 立即返回确认响应
    sendResponseThreadSafe("<ID=304><INFO=OK>", client);

    // 3. 在后台线程中处理FITS文件和计算中值
    QtConcurrent::run([this, camId, fitsHeader, fitsName, client]() {
        // 解析JSON头信息
        QJsonParseError jsonError;
        QJsonDocument doc = QJsonDocument::fromJson(fitsHeader.toUtf8(), &jsonError);
        if (doc.isNull()) {
            QString errorResponse = QString("<ID=304><CamID=%1><INFO=Error><MSG=Invalid_JSON_format><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            sendResponseThreadSafe(errorResponse, client);
            return;
        }

        QJsonObject headerObj = doc.object();

        // 构建完整文件路径
        QString fitsFilePath = QDir(fitsSavePath).absoluteFilePath(fitsName);
        if (!fitsFilePath.endsWith(".fits")) {
            fitsFilePath += ".fits";
        }

        // 检查文件是否存在
        if (!QFile::exists(fitsFilePath)) {
            QString errorResponse = QString("<ID=304><CamID=%1><INFO=Error><MSG=File_not_found><SENDTIME=%2>;")
            .arg(camId)
                .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));
            sendResponseThreadSafe(errorResponse, client);
            return;
        }

        // 计算图像中值（仅对平场类型）
        double fitsMedian = -999.0; // 默认非平场值
        if (headerObj.contains("IMAGETYP")) {
            QString imageType = headerObj["IMAGETYP"].toString().toLower();
            if (imageType == "flat" || imageType == "df" || imageType == "ff") { // 平场类型
                try {
                    fitsMedian = calculateFitsMedian(fitsFilePath);
                    /*LOG_GENERAL(QString("计算平场中值完成 [Cam:%1][File:%2][Median:%3]")
                                 .arg(camId)
                                 .arg(QFileInfo(fitsFilePath).fileName())
                                 .arg(fitsMedian));*/
                    threadSafeLog("INFO", QString("计算平场中值完成 [Cam:%1][Median:%2]")
                                              .arg(camId).arg(fitsMedian));
                } catch (const std::exception& e) {
                    LOG_GENERAL(QString("calculateFitsMedian failed: %1").arg(e.what()));
                }
            }
        }

        // 更新FITS头信息
        bool headerUpdated = updateFitsHeader(fitsFilePath, headerObj);





        // 构造最终响应
        QString response = QString("<ID=304><CamID=%1><INFO=%2><ACTION=DONE><Fits_Median=%3><SENDTIME=%4>;")
                               .arg(camId)
                               .arg(headerUpdated ? "OK" : "Error")
                               .arg(fitsMedian, 0, 'f', 2)
                               .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"));

        // 在主线程发送响应
        QMetaObject::invokeMethod(this, [this, client, response]() {
            sendResponseThreadSafe(response, client);
        });

        // ---- 构造上报 JSON（在主线程里发 POST）----
        QJsonObject uploadData{
            {"fits_path",       fitsFilePath},
            {"db_inserted_id",  1},
            {"info",            "FITS header updated, file saved, and metadata stored."},
            {"status",          200}
        };

        QMetaObject::invokeMethod(qApp, [this, uploadData](){
            postFitsJsonToServer(uploadData);
        }, Qt::QueuedConnection);
    });
}

// MainWindow.cpp
void MainWindow::postFitsJsonToServer(const QJsonObject& obj)
{
    // 确保在主线程且 m_networkManager 就在这个线程里被创建
    Q_ASSERT(thread() == QThread::currentThread());

    QNetworkRequest req;
    // 建议把 URL 做成配置；先用 IPv4 回环避免 IPv6 坑
    req.setUrl(QUrl("http://127.0.0.1:60001/fits"));
    req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    const QByteArray body = QJsonDocument(obj).toJson(QJsonDocument::Compact);
    QNetworkReply* reply = m_networkManager->post(req, body);

    // 更丰富的诊断
    connect(reply, &QNetworkReply::errorOccurred, this,
            [reply,this](QNetworkReply::NetworkError code){
        LOG_CAMERA(QString("304 fits POST error=%1 errStr=%2")
                   .arg(int(code)).arg(reply->errorString()));
        ui->labelCameraStatus_2->setText("304 fits POST failed: " + reply->errorString());
    });

    connect(reply, &QNetworkReply::finished, this, [reply,this](){
        const auto status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        const auto redir  = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
        const auto resp   = QString::fromUtf8(reply->readAll());

        if (reply->error() == QNetworkReply::NoError) {
            LOG_CAMERA(QString("304 fits POST success status=%1 resp=%2").arg(status).arg(resp));
            ui->labelCameraStatus_2->setText("304 fits POST success");
        } else {
            LOG_CAMERA(QString("304 fits POST failed status=%1 err=%2")
                       .arg(status).arg(reply->errorString()));
        }
        if (!redir.isEmpty()) {
            LOG_CAMERA(QString("304 fits POST redirected to: %1").arg(redir.toString()));
        }
        reply->deleteLater();
    });
}

void MainWindow::updateCameraParameters(const QJsonObject &config)
{
    if (config.contains("MODE")) {
        hwStatus.amplifierMode = config["MODE"].toString() == "HC" ? HighCapacity : HighSensitivity;
    }
    if (config.contains("BIT")) {
        hwStatus.bitsPerPixel = config["BIT"].toString().toInt();
    }
    if (config.contains("GAIN")) {
        hwStatus.gainIndex = config["GAIN"].toString().toInt() - 1;
    }
    if (config.contains("BIN")) {
        hwStatus.binning = config["BIN"].toString().toInt();
    }

    LOG_GENERAL("Updated camera parameters:");
    LOG_GENERAL(QString("  Mode: %1").arg(hwStatus.amplifierMode == HighSensitivity ? "HS" : "HC"));
    LOG_GENERAL(QString("  Bits: %1").arg(hwStatus.bitsPerPixel));
    LOG_GENERAL(QString("  Gain: %1").arg(hwStatus.gainIndex + 1));
    LOG_GENERAL(QString("  Binning: %1x%1").arg(hwStatus.binning));

    sendActionCommandToMCU(QString("SET_PARAMS:%1,%2,%3,%4")
                         .arg(hwStatus.amplifierMode == HighSensitivity ? "HS" : "HC")
                         .arg(hwStatus.bitsPerPixel)
                         .arg(hwStatus.gainIndex + 1)
                         .arg(hwStatus.binning));
}

void MainWindow::parseProtocol4720(const QString &command, QTcpSocket *client)
{
    // 解析指令格式: <ID=xxx><PARAM1=value1><PARAM2=value2>;
    QRegularExpression re("<([^>]+)>");
    QRegularExpressionMatchIterator i = re.globalMatch(command);
    LOG_CAMERA("Received OCS command: " + command);

    QMap<QString, QString> params;
    while (i.hasNext()) {
        QRegularExpressionMatch match = i.next();
        QString segment = match.captured(1);
        int eqPos = segment.indexOf('=');
        if (eqPos != -1) {
            QString key = segment.left(eqPos).trimmed();
            QString value = segment.mid(eqPos+1).trimmed();
            params[key] = value;
        }
    }

    // 根据ID分发处理
    QString cmdId = params.value("ID");
    if (cmdId == "602") {
        handleCommand3024720(params, client);
    } else if (cmdId == "603") {
        handleCommand3034720(params, client);
    } else {
        sendResponseThreadSafe(QString("<ID=%1><INFO=Error><MSG=Unsupported_command: %2>").arg(cmdId).arg(cmdId), client);
    }
}

void MainWindow::handleCommand3024720(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 1. 参数校验
    if (!params.contains("CamID") || !params.contains("IMAGENAME") ||
        !params.contains("TIME") || !params.contains("EXP_TYPE") ||
        !params.contains("IMG_INFO")) {
        sendResponseThreadSafe("<ID=602><INFO=Error><MSG=Missing_parameters>", client);
        return;
    }

    // 2. 检查相机状态
    //if (cameraState.status != Idle) {
        //sendResponseThreadSafe("<ID=302><INFO=Error><MSG=Camera is busy>", client);
        //return;
    //}

    // 清空已接收的tag
    m_receivedTags4720.clear();
    m_convertedTags4720.clear();

    // 3. 解析参数
    QString camId = params["CamID"];
    QString imageName = params["IMAGENAME"];
    double expTime = params["TIME"].toDouble();
    QString expType = params["EXP_TYPE"];
    QString imgInfo = params["IMG_INFO"];
    bool dump = params.value("DUMP", "1") == "1";
    bool pinning = params.value("PINNING", "1") == "1";


    int index = -1;
    if (expType == "sc") {
        index = ui->comBoxframetype_2->findData("sc");
    } else if (expType == "bs") {
        index = ui->comBoxframetype_2->findData("bs");
    } else if (expType == "dk") {
        index = ui->comBoxframetype_2->findData("dk");
    } else if (expType == "ts") {
        index = ui->comBoxframetype_2->findData("ts");
    } else if (expType == "sf") {
        index = ui->comBoxframetype_2->findData("sf");
    } else if (expType == "df") {
        index = ui->comBoxframetype_2->findData("df");
    } else if (expType == "fs") {
        index = ui->comBoxframetype_2->findData("fs");
    }
    if (index != -1) {
        QMetaObject::invokeMethod(this, [this, index]() {
            ui->comBoxframetype_2->setCurrentIndex(index);
        }, Qt::QueuedConnection);
    }

    // 4. 立即返回确认响应
    sendResponseThreadSafe("<ID=602><INFO=OK>", client);

    // 5. 记录曝光开始时间（ISO 8601格式）
    QDateTime expStartTime = QDateTime::currentDateTime();
    QString expStartTimeStr = expStartTime.toString("yyyy-MM-dd hh:mm:ss.zzz");

    // 6. 设置曝光参数
    currentBaseFilename4720 = imageName;
    ui->lineEditBaseFilename_2->setText(currentBaseFilename4720);
    ui->doubleSpinBoxExposureTime_2->setValue(expTime);
    currentExposureTime4720 = expTime;
    currentProgressTotalTime4720 = expTime + 0.1; // 曝光时间估算
    progressStartTime4720 = QDateTime::currentMSecsSinceEpoch();

    // 7. 解析并保存IMG_INFO
    QJsonParseError jsonError;
    QJsonDocument doc = QJsonDocument::fromJson(imgInfo.toUtf8(), &jsonError);
    if (!doc.isNull() && doc.isObject()) {
        QJsonObject imgInfoObj = doc.object();
        if (imgInfoObj.contains("IMG_NUM")) {
            // 更新UI中的IMG_NUM显示
            LOG_CAMERA(QString("enter set img_num: [") + imgInfoObj["IMG_NUM"].toString() + "]");
            QString imgNum = imgInfoObj["IMG_NUM"].toString();
            ui->lineEditImgNum_2->setText(imgNum);
            // LOG_CAMERA(imgNum);
        }
        if (imgInfoObj.contains("OBJ_NAME")) {
            QString objectname = imgInfoObj["OBJ_NAME"].toString();
            ui->lineEditObjName_2->setText(objectname);
        }
        if (imgInfoObj.contains("FW_NAME")) {
            QString fwname = imgInfoObj["FW_NAME"].toString();
            ui->lineEditFwName_2->setText(fwname);
        }
        // 同时更新本地计数器，保持同步
        //m_localImageCounter = imgNum.toInt();
        hwStatus.currentImgInfo = imgInfoObj;
    } else {
        LOG_CAMERA(QString("IMG_INFO Parsing failed: %1").arg(jsonError.errorString()));
    }

    int darkMode = (expType == "bs" || expType == "dk") ? 1 : 0;

    // 0814 add
    fitsSavePath4720 = ui->lineEditFitsPath_2->text();
    QString fitsbasename = imageName;
    if(fitsbasename.endsWith(".fits")) {
        fitsbasename.chop(5);
    }
    currentBaseFilename4720 = fitsbasename;
    ui->lineEditBaseFilename_2->setText(currentBaseFilename4720);
    QString fitspathcmd = QString("get_frame_290_subps:$filename=%1$path=%2").arg(fitsbasename).arg(fitsSavePath4720);
    /*sendCommandToSlave1(fitspathcmd, statusLeds.slave[0].ip);
    sendCommandToSlave2(fitspathcmd, statusLeds.slave[1].ip);
    sendCommandToSlave3(fitspathcmd, statusLeds.slave[2].ip);
    sendCommandToSlave4(fitspathcmd, statusLeds.slave[3].ip);*/

    // 8. 发送相机曝光指令（通过UDP）
    QString tag = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    QString expCmd = QString("get_frame_4720:$tag=%1$dark=%2$pinning=%3$dump=%4$exp=%5")
                         .arg(tag)
                         .arg(darkMode)  // 暗场标记
                         .arg(pinning ? 1 : 0)
                         .arg(dump ? 1 : 0)
                         .arg(expTime, 0, 'f', 4);
    sendCameraActionCommand4720(expCmd);

    m_currentExpInfo4720.isManual = false;
    m_currentExpInfo4720.readoutRate = ui->labelCurrentReadoutRate_2->text();
    m_currentExpInfo4720.setexptime = expTime;

    // 存储响应信息
    QString tag1 = QDateTime::currentDateTime().toString("yyMMddhhmmss");
    m_pendingResponse4720 = {
        client,
        params["CamID"],
        params["TIME"].toDouble(),
        QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"),
        params["IMAGENAME"]
    };

    // 9. 更新状态
    //hwStatus.cameraState = Exposing;
    // 初始化进度控制
    progressStartTime4720 = QDateTime::currentMSecsSinceEpoch();
    currentExposureTime4720 = expTime;

    // 设置进度条范围
    ui->ExposureprogressBar_2->setRange(0, 100);
    ui->ExposureprogressBar_2->setValue(0);

    // 启动定时器
    progressTimer4720->start(200); // 200ms更新一次

    // 10. 记录日志
    LOG_CAMERA(QString("Start exposure [Cam:%1][Exp:%2s][Type:%3][File:%4]")
                   .arg(camId).arg(expTime).arg(expType).arg(imageName));
}



void MainWindow::handleCommand3034720(const QMap<QString, QString> &params, QTcpSocket *client)
{
    // 格式: <ID=303><CamID=25078>
    if (!params.contains("CamID")) {
        sendResponseThreadSafe("<ID=603><INFO=Error><MSG=Missing_CamID>", client);
        return;
    }

    // 1. 返回确认响应
    sendResponseThreadSafe("<ID=603><INFO=OK>", client);

    // 2. 发送中止曝光指令
    sendCameraActionCommand4720("abort_4720");

    // 3. 更新状态
    //hwStatus.cameraState = MainWindow::Idle;
    progressTimer4720->stop();
    ui->ExposureprogressBar_2->setValue(0);

    // 4. 返回完成响应
    QString response = QString("<ID=603><CamID=%1><INFO=OK><ACTION=DONE><SENDTIME=%2>;")
                           .arg(params["CamID"])
                           .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmss"));
    sendResponseThreadSafe(response, client);
}







